package runze.util.base;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Dao实现模板
 *
 * 为方便数据迁移和分库分表，
 * 需要生成唯一id：新建一个专用生成id的自增长表
 *
 * @author wrzhxy@qq.com
 * @创建时间 2017年2月15日 上午11:21:05
 */
@SuppressWarnings({"unchecked"})
public abstract class HibernateDaoImp<T> implements IDao<T> {
	private Class<T> targetEntity;
	
	public HibernateDaoImp() {
		Type genType = getClass().getGenericSuperclass();   
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();   
		targetEntity =  (Class<T>)params[0];
	}
	
	private static SessionFactory sessionFactory;	// TODO 线程安全？需测试
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		HibernateDaoImp.sessionFactory = sessionFactory;
	}

    /**
     * 自增长版添加
     * @param   t 要增加的实体
     * @return
     */
	@Override
	public long add(T t) {
		Session session = sessionFactory.getCurrentSession();
		return (Long) session.save(t);
	}

    /**
     * uuid 版添加
     * @param   t 要增加的实体
     * @return  uuid
     */
	@Override
    public String add_uuid(T t) {
        Session session = sessionFactory.getCurrentSession();
        return (String) session.save(t);
    }

    /**
     * 删除
     * @param id 主键自增长id
     */
	@Override
	public void del(long id) {
		Session session = sessionFactory.getCurrentSession();
		T load = session.load(targetEntity, id);
		session.delete(load);
	}

    /**
     * 删除
     * @param id 主键uuid
     */
    @Override
    public void del(String id) {
        Session session = sessionFactory.getCurrentSession();
        T load = session.load(targetEntity, id);
        session.delete(load);
    }

    /**
     * 更新
     * @param t 更新实体
     */
	@Override
	public void update(T t) {
		Session session = sessionFactory.getCurrentSession();
		session.update(t);
	}

    /**
     * 按主键获取单个实体
     * @param id
     * @return 单个实体
     */
    @Override
    public T get(long id) {
        Session session = sessionFactory.getCurrentSession();
        return session.get(targetEntity, id);
    }
    /**
     * uuid版按主键
     * @param id
     * @return 单个实体
     */
    @Override
    public T get(String id) {
        Session session = sessionFactory.getCurrentSession();
        return session.get(targetEntity, id);
    }
	/**
     * 按条件获取单个实体
     * @param sql       sql语句
     * @param params    sql参数
     * @return          单个实体
     */
    @Override
    public T get(String sql, Object... params) {
        List<T> list = find(sql, params);
        if (list == null || list.size() < 1)
            return null;

        return list.get(0);
    }

    /**
     * 对外暴露find方法作为所有查找方法的接口
     * 底层封装find和findByPage
     */
    /**
	 * 获取所有实体
	 * @return 所有实体的集合
	 */
	@Override
    public List<T> find() {
		Session session = sessionFactory.getCurrentSession();
		List<T> list = (List<T>) session.createQuery("from "
				+ targetEntity.getSimpleName()).list();
		if (list != null && list.size() < 1) list = null;
		return list;
	}

    /**
     * 按语句获取所有实体（不分页）
     * @param sql		sql语句
     * @param params	语句中的参数
     * @return			按条件获取到的实体的集合
     */
    @Override
    public List<T> find(String sql, Object... params) {
        Session session = sessionFactory.getCurrentSession();
        org.hibernate.Query query = session.createQuery(sql);
        for (int i = 0, len = params.length; i < len; i++) {
            query.setParameter(i, params[i]);
        }
        List<T> list = (List<T>) query.list();
        if (list != null && list.size() < 1) list = null;
		return list;
    }

    /**
     * 按分页获取实体
     *
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public List<T> find(int pageNo, int pageSize) {
        return find(new Query(pageNo, pageSize));
    }

    /**
     * 按分页获取实体 通过无参数语句筛选
     *
     * @param pageNo
     * @param pageSize
     * @param sql
     * @return
     */
    @Override
    public List<T> find(int pageNo, int pageSize, String sql) {
        return find(new Query(pageNo, pageSize), sql);
    }

    /**
     * 按分页获取实体 通过有参数语句筛选
     *
     * @param pageNo
     * @param pageSize
     * @param sql
     * @param params
     * @return
     */
    @Override
    public List<T> find(int pageNo, int pageSize, String sql, Object... params) {
        return find(new Query(pageNo, pageSize), sql, params);
    }

    /**
     * 按分页获取实体
     *
     * 通过Page类的PageNo, PageSize获取实体集合
     * @param   query
     * @return  实体的集合
     */
    @Override
    public List<T> find(Query query) {
        getQuery(query);

        int pageNo = query.getPageNo();
        int pageSize = query.getPageSize();

        Session session = sessionFactory.getCurrentSession();
        String sql = "from " + targetEntity.getSimpleName() + " order by id desc";  // TODO uuid排序有问题
        List<T> list = session.createQuery(sql)
                .setFirstResult((pageNo - 1) * pageSize)
                .setMaxResults(pageSize).list();
        if (list != null && list.size() < 1) list = null;
		return list;
    }

    /**
     * 按分页获取实体 通过无参数语句筛选
     * @param query
     * @param sql       sql语句
     * @return          实体集合
     */
    @Override
    public List<T> find(Query query, String sql) {
        getQuery(query);

        int pageNo = query.getPageNo();
        int pageSize = query.getPageSize();

        Session session = sessionFactory.getCurrentSession();
        List<T> list = session.createQuery(sql)
                .setFirstResult((pageNo - 1) * pageSize)
                .setMaxResults(pageSize)
                .list();
        if (list != null && list.size() < 1) list = null;
		return list;
    }

    /**
     * 按分页获取实体 通过有参数语句筛选
     * @param query
     * @param sql       sql语句
     * @param params    sql参数
     * @return          实体集合
     */
    @Override
    public List<T> find(Query query, String sql, Object... params) {
        getQuery(query);

        int pageNo = query.getPageNo();
        int pageSize = query.getPageSize();

        Session session = sessionFactory.getCurrentSession();
        org.hibernate.Query q = session.createQuery(sql);
        int len = params.length;
        for (int i = 0; i < len; i++) {
            q.setParameter(i, params[i]);
        }
        List<T> list = q.setFirstResult((pageNo - 1) * pageSize)
                .setMaxResults(pageSize).list();
        if (list != null && list.size() < 1) list = null;
		return list;
    }

	@Override
	public long getTotal() {
		Session session = sessionFactory.getCurrentSession();
		org.hibernate.Query query = session.createQuery("from " + targetEntity.getSimpleName());
		ScrollableResults scroll = query.scroll();
		scroll.last();
		return scroll.getRowNumber() + 1;
	}

	@Override
	public long getTotal(String sql) {
		Session session = sessionFactory.getCurrentSession();
		org.hibernate.Query query = session.createQuery(sql);
		ScrollableResults scroll = query.scroll();
		scroll.last();
		return scroll.getRowNumber() + 1;
	}

	@Override
	public long getTotal(String sql, Object... params) {
		Session session = sessionFactory.getCurrentSession();
		org.hibernate.Query query = session.createQuery("select count(id) " + sql);
		int len = params.length;
		for (int i = 0; i < len; i++) {
			query.setParameter(i, params[i]);
		}
//		ScrollableResults scroll = query.scroll();
//		scroll.last();
		return (long) query.list().get(0);
	}

    @Override
	public long getTotal(Query query) { return getTotal(); }

    private Query getQuery(Query query) {
        if (query == null) query = new Query();
//        query.setPageNo(query.getPageNo() > 1 ? query.getPageNo() : 1);
//        query.setPageSize(query.getPageSize() > 1 ? query.getPageSize() : 10);

        return query;
    }
	protected Object[] listToParams(List<Object> objs) {
		Object[] params = new Object[objs.size()];
		for (int i = 0; i < objs.size(); i++) {
			params[i] = objs.get(i);
		}
		return params;
	}
}
